package com.lnlyj.navigation;

import android.content.Context;
import android.content.res.ColorStateList;
import android.content.res.TypedArray;
import android.database.DataSetObserver;
import android.graphics.Color;
import android.graphics.drawable.Drawable;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.view.PagerAdapter;
import android.support.v4.view.ViewPager;
import android.util.AttributeSet;
import android.util.Log;
import android.util.TypedValue;
import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;


import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 * Create By Lnlyj on 2019/11/25
 **/
public class LnlyjNavigationBar extends LinearLayout {

    private static final String TAG = "LnlyjNavigationBar";

    private static final int TAB_DEFAULT_TEXT_COLOR = Color.BLACK;
    private static final int UNKNOWN_RESOURCE_ID = -1;

    private List<TabView> tabs = new ArrayList<>();

    private List<CharSequence> tabTitles = new ArrayList<>();
    private List<Integer> tabIcons = new ArrayList<>();
    private int tabSelectedColor;
    private int tabDefaultColor;
    private int tabTextSize;

    private int selectedIndex = 0;

    private ViewPager viewPager;
    private LnlyjNavigationBarOnPageChangeListener pageChangeListener;
    private PagerAdapter pagerAdapter;
    private DataSetObserver pagerAdapterObserver;
    private AdapterChangeListener adapterChangeListener;

    private List<OnTabChangeListener> tabChangeListeners = new ArrayList<>();

    public interface OnTabChangeListener {
        void onTabChange(int oldIndex, int newIndex);
    }

    public LnlyjNavigationBar(Context context) {
        this(context, null);
    }

    public LnlyjNavigationBar(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public LnlyjNavigationBar(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(context, attrs);
        setOrientation(HORIZONTAL);
    }

    @Override
    public void addView(View child, int index, ViewGroup.LayoutParams params) {
        if (child instanceof TabView) {
            super.addView(child, index, params);
        }
        Log.d(TAG, "addView: Cannot add a child view");
    }

    @Override
    protected void onDetachedFromWindow() {
        super.onDetachedFromWindow();
        removeAllTabChangeListener();
    }

    private void init(Context context, @Nullable AttributeSet attrs) {
        TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.LnlyjNavigationBar);

        int tabStringResId = ta.getResourceId(R.styleable.LnlyjNavigationBar_tab_array, UNKNOWN_RESOURCE_ID);
        int tabDrawableResId = ta.getResourceId(R.styleable.LnlyjNavigationBar_tab_drawable, UNKNOWN_RESOURCE_ID);
        tabSelectedColor = ta.getColor(R.styleable.LnlyjNavigationBar_title_text_selected_color, TAB_DEFAULT_TEXT_COLOR);
        tabDefaultColor = ta.getColor(R.styleable.LnlyjNavigationBar_title_text_default_color, TAB_DEFAULT_TEXT_COLOR);
        tabTextSize = ta.getDimensionPixelSize(R.styleable.LnlyjNavigationBar_tab_text_size, UNKNOWN_RESOURCE_ID);
        selectedIndex = ta.getInteger(R.styleable.LnlyjNavigationBar_default_selected_index, 0);

        if (tabStringResId != UNKNOWN_RESOURCE_ID) {
            CharSequence[] ts = context.getResources().getStringArray(tabStringResId);
            tabTitles.addAll(Arrays.asList(ts));
        }

        if (tabDrawableResId != UNKNOWN_RESOURCE_ID) {
            TypedArray t = context.getResources().obtainTypedArray(tabDrawableResId);
            int count  = t.length();
            for (int i = 0; i < count; i++) {
                tabIcons.add(t.getResourceId(i, 0));
            }
            t.recycle();
        }

        ta.recycle();

        createTab();
        if (getTabCount() > 0) {
            if (selectedIndex >= getTabCount()) {
                selectedIndex = getTabCount() - 1;
            }
            callTabSelected(selectedIndex);
        }
    }

    private void createTab() {
        removeAllViews();
        tabs.clear();
        int count = tabTitles.size();
        for (int i = 0; i < count; i++) {
            tabs.add(newTabView(i, -1));
        }
    }

    private TabView newTabView(int position, int addIndex) {
        boolean addImage = tabTitles.size() == tabIcons.size();

        Drawable drawable = addImage ? (tabIcons.get(position) > 0 ? getContext().getResources().getDrawable(tabIcons.get(position)) : null) : null;

        Tab tab = new Tab(tabTitles.get(position), drawable, tabSelectedColor, tabDefaultColor, tabTextSize);
        TabView tabView = new TabView(getContext(), tab);
        tabView.setTag(position);
        tabView.setOnClickListener(onTabClickListener);
        LayoutParams layoutParams = new LayoutParams(0, ViewGroup.LayoutParams.MATCH_PARENT);
        layoutParams.weight = 1;

        this.addView(tabView, addIndex, layoutParams);

        return tabView;
    }

    private TabView newTabView(CharSequence title, int icon, int addIndex) {

        this.tabTitles.add(title);
        this.tabIcons.add(icon);

        int position = tabTitles.size() - 1;
        return newTabView(position, addIndex);
    }

    private OnClickListener onTabClickListener = new OnClickListener() {
        @Override
        public void onClick(View v) {
            int index = tabs.indexOf(v);

            if (index == selectedIndex) {
                return;
            }

            if (viewPager != null) {

                int curItem = viewPager.getCurrentItem();
                PagerAdapter adapter = viewPager.getAdapter();

                if (adapter != null && index < adapter.getCount() && curItem != index) {
                    viewPager.setCurrentItem(index);
                } else {
                    callTabSelected(index);
                }
            } else {
                callTabSelected(index);
            }

        }
    };

    private void callTabSelected(int index) {
        int oldSelectedIndex = selectedIndex;

        if (selectedIndex >=0 && selectedIndex < getTabCount()) {
            getTabViewAtIndex(selectedIndex).setSelected(false);
        }

        selectedIndex = index;

        if (selectedIndex >=0 && selectedIndex < getTabCount()) {
            getTabViewAtIndex(selectedIndex).setSelected(true);
        }

        for (OnTabChangeListener listener : tabChangeListeners) {
            listener.onTabChange(oldSelectedIndex, selectedIndex);
        }
    }

    private TabView getTabViewAtIndex(int index) {
        return tabs.get(index);
    }

    void setPagerAdapter(@Nullable PagerAdapter adapter, boolean addObserver) {
        if (this.pagerAdapter != null && this.pagerAdapterObserver != null) {
            this.pagerAdapter.unregisterDataSetObserver(this.pagerAdapterObserver);
        }

        this.pagerAdapter = adapter;
        if (addObserver && adapter != null) {
            if (this.pagerAdapterObserver == null) {
                this.pagerAdapterObserver = new PagerAdapterObserver();
            }

            adapter.registerDataSetObserver(this.pagerAdapterObserver);
        }

        this.populateFromPagerAdapter();
    }

    void populateFromPagerAdapter() {
        this.removeAllViews();
        tabTitles.clear();
        tabIcons.clear();
        if (this.pagerAdapter != null) {
            int adapterCount = this.pagerAdapter.getCount();
            boolean isLnlyjAdapter = this.pagerAdapter instanceof LnlyjNavigationPageAdapter;

            int curItem;
            for(curItem = 0; curItem < adapterCount; ++curItem) {
                if (isLnlyjAdapter) {
                    this.tabTitles.add(((LnlyjNavigationPageAdapter) pagerAdapter).getTabTitle(curItem));
                    this.tabIcons.add(((LnlyjNavigationPageAdapter) pagerAdapter).getTabDrawable(curItem));
                } else {
                    this.tabTitles.add(pagerAdapter.getPageTitle(curItem));
                }
            }
            createTab();
            if (this.viewPager != null && adapterCount > 0) {
                curItem = viewPager.getCurrentItem();
                selectedIndex = -1;
                if (curItem < adapterCount) {
                    callTabSelected(curItem);
                }
            }
        }

    }

    // public method

    /**
     * 设置选中颜色
     * @param color 颜色
     */
    public void setTabSelectedColor(int color) {
        this.tabSelectedColor = color;
        for (TabView tabView : tabs) {
            tabView.tab.selectedColor = color;
            tabView.updateView();
        }
    }

    /**
     * 设置默认颜色（未选中颜色）
     * @param color 颜色
     */
    public void setTabDefaultColor(int color) {
        this.tabDefaultColor = color;
        for (TabView tabView : tabs) {
            tabView.tab.defaultColor = color;
            tabView.updateView();
        }
    }

    /**
     * 设置字体大小
     * @param size 字体大小
     */
    public void setTabTextSize(int size) {
        this.tabTextSize = size;
        for (TabView tabView : tabs) {
            tabView.tab.textSize = size;
            tabView.updateView();
        }
    }

    /**
     * 设置选中Tab
     * @param index 第几项
     */
    public void setSelectedIndex(int index) {
		if (viewPager != null) {
			int curItem = viewPager.getCurrentItem();
			PagerAdapter adapter = viewPager.getAdapter();

			if (adapter != null && index < adapter.getCount() && curItem != index) {
				viewPager.setCurrentItem(index);
			} else {
				callTabSelected(index);
			}
		} else {
			if (index >= 0 && index < getTabCount()) {
				callTabSelected(index);
			}
		}
			
    }

    /**
     * 获取当前选中索引
     * @return index
     */
    public int getSelectedIndex() {
        return selectedIndex;
    }

    /**
     * 获取当前有多少个Tab
     * @return 例如：10
     */
    public int getTabCount() {
        return tabs.size();
    }

    /**
     * 根据viewpage进行初始化tab
     *
     * <li>
     *     item标题：{@link PagerAdapter#getPageTitle(int)}
     *     item图标：{@link LnlyjNavigationPageAdapter#getPageDrawable(int)}
     * </li>
     *
     * @param viewPager viewpage 控件
     * @param relationAdapter 是否加入adapter change变化监听
     *
     * @see LnlyjNavigationPageAdapter#getPageDrawable(int)
     */
    public void setupWithViewPager(@Nullable ViewPager viewPager, boolean relationAdapter) {
        if (this.viewPager != null) {
            if (this.pageChangeListener != null) {
                this.viewPager.removeOnPageChangeListener(this.pageChangeListener);
            }

            if (relationAdapter && this.adapterChangeListener != null) {
                this.viewPager.removeOnAdapterChangeListener(this.adapterChangeListener);
            }
        }

        if (viewPager != null) {
            this.viewPager = viewPager;
            if (this.pageChangeListener == null) {
                this.pageChangeListener = new LnlyjNavigationBarOnPageChangeListener(this);
            }

            this.pageChangeListener.reset();
            viewPager.addOnPageChangeListener(this.pageChangeListener);

            PagerAdapter adapter = viewPager.getAdapter();
            if (adapter != null) {
                this.setPagerAdapter(adapter, true);
            }

            if (this.adapterChangeListener == null) {
                this.adapterChangeListener = new AdapterChangeListener();
            }

            this.adapterChangeListener.setAutoRefresh(true);
            viewPager.addOnAdapterChangeListener(this.adapterChangeListener);

        } else {
            this.viewPager = null;
            this.setPagerAdapter((PagerAdapter)null, false);
        }
    }

    /**
     * 根据标题和图标进行初始化tab
     * @param tabTitles 标题集合
     * @param tabIcons 图标集合
     * @param selectedIndex 选中的tab
     */
    public void setupWithTitlesAndIcons(List<CharSequence> tabTitles, List<Integer> tabIcons, int selectedIndex) {
        this.tabTitles.addAll(tabTitles);
        this.tabIcons.addAll(tabIcons);
        createTab();
        callTabSelected(selectedIndex);
    }

    /**
     * 根据标题和图标进行初始化tab
     * @param tabTitles 标题集合
     * @param tabIcons 图标集合
     */
    public void setupWithTitlesAndIcons(List<CharSequence> tabTitles, List<Integer> tabIcons) {
        setupWithTitlesAndIcons(tabTitles, tabIcons, 0);
    }

    /**
     * 添加一个Tab
     * @param title 标题
     * @param icon 图标
     * @param isSelected 是否选中
     */
    public void  addTab(CharSequence title, int icon, int addIndex, boolean isSelected) {
        if (viewPager != null) {
            return;
        }
        if (addIndex < 0) {
            addIndex = 0;
        }
        tabs.add(addIndex, newTabView(title, icon, addIndex));
        if (isSelected) {

            if (addIndex <= selectedIndex) {
                selectedIndex += 1;
            }

            callTabSelected(addIndex);
        }
    }

    /**
     * 添加监听
     * @param listener 监听器
     */
    public void addTabChangeListener(OnTabChangeListener listener) {
        addTabChangeListener(listener, false);
    }

    /**
     * 添加监听
     * @param listener 监听器
     * @param callListener 是否立即调用回调，调用时，oldIndex为-1
     */
    public void addTabChangeListener(OnTabChangeListener listener, boolean callListener) {
        if (!this.tabChangeListeners.contains(listener)) {
            this.tabChangeListeners.add(listener);
        }
        if (callListener) {
            listener.onTabChange(-1, selectedIndex);
        }
    }

    /**
     * 移除监听
     * @param listener 监听器
     */
    public void removeTabChangeListener(OnTabChangeListener listener) {
        this.tabChangeListeners.remove(listener);
    }

    /**
     * 移除所有监听
     */
    public void removeAllTabChangeListener() {
        this.tabChangeListeners.clear();
        Log.d(TAG, "removeAllTabChangeListener");
    }

    // private class

    private class AdapterChangeListener implements ViewPager.OnAdapterChangeListener {
        private boolean autoRefresh;

        AdapterChangeListener() {
        }

        public void onAdapterChanged(@NonNull ViewPager viewPager, @Nullable PagerAdapter oldAdapter, @Nullable PagerAdapter newAdapter) {
            if (LnlyjNavigationBar.this.viewPager == viewPager) {
                LnlyjNavigationBar.this.setPagerAdapter(newAdapter, this.autoRefresh);
            }

        }

        void setAutoRefresh(boolean autoRefresh) {
            this.autoRefresh = autoRefresh;
        }
    }

    private class PagerAdapterObserver extends DataSetObserver {
        PagerAdapterObserver() {
        }

        public void onChanged() {
            LnlyjNavigationBar.this.populateFromPagerAdapter();
        }

        public void onInvalidated() {
            LnlyjNavigationBar.this.populateFromPagerAdapter();
        }
    }

    private static class LnlyjNavigationBarOnPageChangeListener implements ViewPager.OnPageChangeListener {
        private final WeakReference<LnlyjNavigationBar> navigationRef;
        private int previousScrollState;
        private int scrollState;

        public LnlyjNavigationBarOnPageChangeListener(LnlyjNavigationBar navigationBar) {
            this.navigationRef = new WeakReference(navigationBar);
        }

        public void onPageScrollStateChanged(int state) {
            this.previousScrollState = this.scrollState;
            this.scrollState = state;
        }

        public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {

        }

        public void onPageSelected(int position) {
            LnlyjNavigationBar navigationBar = this.navigationRef.get();
            if (navigationBar != null && navigationBar.selectedIndex != position && position < navigationBar.getChildCount()) {
                navigationBar.callTabSelected(position);
            }
        }

        void reset() {
            this.previousScrollState = this.scrollState = 0;
        }
    }

    private static class Tab {

        int selectedColor;
        int defaultColor;
        int textSize;
        CharSequence title;
        Drawable drawable;

        public Tab(CharSequence title, Drawable drawable, int selectedColor, int defaultColor, int textSize) {
            this.title = title;
            this.drawable = drawable;
            this.selectedColor = selectedColor;
            this.defaultColor = defaultColor;
            this.textSize = textSize;
        }
    }

    private static class TabView extends LinearLayout {

        Context context;
        Tab tab;

        TextView title;
        ImageView icon;

        public TabView(Context context, Tab tab) {
            super(context);
            this.context = context;
            this.tab = tab;
            setOrientation(VERTICAL);
            setGravity(Gravity.CENTER);
            initView();
        }

        private void initView() {

            if (tab.drawable != null) {
                icon = new ImageView(context);
                icon.setImageDrawable(tab.drawable);
                icon.setImageTintList(createColorStateList(tab.selectedColor, tab.selectedColor, tab.defaultColor));
                LayoutParams lp = new LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
                lp.bottomMargin = 4;
                addView(icon, lp);
            }

            title = new TextView(context);
            title.setSingleLine();
            title.setText(tab.title);
            if (tab.textSize > 0) {
                title.setTextSize(TypedValue.COMPLEX_UNIT_PX, tab.textSize);
            }
            title.setTextColor(createColorStateList(tab.selectedColor, tab.selectedColor, tab.defaultColor));
            addView(title, new LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT));
        }

        public void updateView() {
            if (icon != null) {
                icon.setImageDrawable(tab.drawable);
                icon.setImageTintList(createColorStateList(tab.selectedColor, tab.selectedColor, tab.defaultColor));
            }

            if (title != null) {
                if (tab.textSize > 0) {
                    title.setTextSize(TypedValue.COMPLEX_UNIT_PX, tab.textSize);
                }
                title.setTextColor(createColorStateList(tab.selectedColor, tab.selectedColor, tab.defaultColor));
            }
        }

        private ColorStateList createColorStateList(int pressColor, int selectedColor, int normalColor) {
            int[][] states = new int[3][];
            int[] colors = new int[]{pressColor, selectedColor, normalColor};
            int[] press = new int[]{android.R.attr.state_pressed};
            int[] selected = new int[]{android.R.attr.state_selected};
            int[] normal = new int[]{};
            states[0] = press;
            states[1] = selected;
            states[2] = normal;

            return new ColorStateList(states, colors);
        }
    }

}
